package ensemble.samples.custom.sqlformat.javafx.util;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.collect.ImmutableMap;

/**
 * @author duanxq
 * @date 2020/8/10
 */
public class MybatisSqlFormat {

    public static final String DATABASE_TYPE_MYSQL = "MYSQL";
    public static final String DATABASE_TYPE_ORACLE = "ORACLE";

    public static String getSqlFromStr(String input) {
        return getSqlFromStr(input, DATABASE_TYPE_MYSQL);
    }

    /**
     * 数据库类型
     */
    public static String getSqlFromStr(String input, String databaseType) {
        if (input == null || input.isEmpty()) {
            return input;
        }
        if (!input.contains("\n")) {
            return input;
        }
        String sql = getSqlFromInput(input);
        String param = getParamFromInput(input);
        if (sql == null || param == null) {
            return input;
        }
        List<String> paramList = getParamList(param, databaseType);
        for (String s : paramList) {
            if (!sql.contains("?")) {
                return "arguments not match";
            }
            sql = sql.replaceFirst("\\?", s);
        }
        return sql;
    }

    private static String getSqlFromInput(String input) {
        Pattern compile = Pattern.compile("Preparing: (.*)");
        Matcher matcher = compile.matcher(input);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    private static String getParamFromInput(String input) {
        Pattern compile = Pattern.compile("Parameters: (.*)");
        Matcher matcher = compile.matcher(input);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;

    }

    private static List<String> getParamList(String param, String databaseType) {
        String[] split = param.split(", ");
        List<String> result = new ArrayList<>();
        for (String s : split) {
            String value = s;
            if (s.contains("(")) {
                value = s.substring(0, s.indexOf("("));
            }
            result.add(getFunction(s, FORMAT_MAP.get(databaseType)).apply(value));
        }
        return result;
    }

    private static Function<String, String> getFunction(String param,
        Map<String, Function<String, String>> functionMap) {
        return functionMap.entrySet().stream()
            .filter(k -> param.contains(k.getKey()))
            .map(Map.Entry::getValue)
            .findFirst()
            .orElseThrow(UnsupportedOperationException::new);
    }

    private final static ImmutableMap<String, Function<String, String>> MYSQL_MAP =
        new ImmutableMap.Builder<String,
        Function<String, String>>()
        .put("(Integer", Function.identity())
        .put("(Long", Function.identity())
        .put("(BigDecimal", Function.identity())
        .put("(Boolean", Function.identity())
        .put("(String", e -> String.format("'%s'", e))
        .put("", Function.identity())
        .build();
    private final static ImmutableMap<String, Function<String, String>> ORACLE_MAP = new ImmutableMap.Builder<String, Function<String, String>>()
        .put("(Integer", Function.identity())
        .put("(Long", Function.identity())
        .put("(BigDecimal", Function.identity())
        .put("(Boolean", Function.identity())
        .put("(Timestamp", e -> String.format("to_timestamp('%s', 'yyyy-MM-dd HH24:MI:ss.ff')", e))
        .put("(Date", e -> String.format("to_date('%s', 'yyyy-MM-dd HH24:MI:ss')", e))
        .put("(String", e -> String.format("'%s'", e))
        .put("", Function.identity())
        .build();
    private final static ImmutableMap<String, Map<String, Function<String, String>>> FORMAT_MAP = new ImmutableMap.Builder<String, Map<String, Function<String, String>>>()
        .put(DATABASE_TYPE_MYSQL, MYSQL_MAP)
        .put(DATABASE_TYPE_ORACLE, ORACLE_MAP)
        .build();


}
